/*
 * Copyright 2008-2010 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package jrebel.agent;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * This is the main class for BTrace java.lang.instrument agent.
 * 
 * @author A. Sundararajan
 * @authos Joachim Skeie (rolling output)
 */
public final class Main {
	private static volatile Instrumentation inst;
	private static volatile boolean debugMode;
	private static volatile boolean dumpClasses;
	private static volatile String dumpDir;
	private static ServerSocket serverSocket;
	private static volatile boolean exit = false;
	private static volatile Socket client;
	public static volatile String password = "zniu";
	public static int port;

	private static void registerExitHook(final RemoteClient c) {
		Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
			public void run() {
				if (client != null)
					;;//rt.handleExit(0);
			}
		}));
	}

	public static void premain(String args, Instrumentation inst) {
		main(args, inst);
	}

	public static void agentmain(String args, Instrumentation inst) {
		main(args, inst);
	}

	private static synchronized void main(final String args,
			final Instrumentation inst) {
		if (Main.inst != null) {
			error("error, did not get Instrumentation instance, exit! ");
			return;
		}
		Main.port = getPort(args) ;
		Main.inst = inst;
		//Main.startServer() ;
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				startServer();
			}
		}).start();
	}
	public static int getPort(String arg ){
		String p = arg.substring(arg.indexOf("port=")+"port=".length()) ;
		return Integer.parseInt(p) ;
	}

	private static void usage() {
		// System.out.println(Messages.get("btrace.agent.usage"));
		System.exit(0);
	}


	// -- Internals only below this point
	private static void startServer() {
		try {
			debugPrint("starting server at " + port);
			serverSocket = new ServerSocket(port);
		} catch (IOException ioexp) {
			ioexp.printStackTrace();
			return;
		}
		while (!exit) {
			try {
				debugPrint("waiting for clients");
				Socket sock = serverSocket.accept();
				RemoteClient client = new RemoteClient(inst, sock);
				debugPrint("start server for client " + sock);
				//client.start();
				// registerExitHook(client);
				client.run() ;
			} catch (RuntimeException re) {
				debugPrint(re);
			} catch (IOException ioexp) {
				debugPrint(ioexp);
			}
		}
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	private static void error(String msg) {
		System.err.println(msg);
	}

	static void dumpClass(String btraceClassName, String targetClassName,
			byte[] code) {
		if (dumpClasses) {
			try {
				targetClassName = targetClassName.replace(".", File.separator)
						.replace("/", File.separator);
				int index = targetClassName.lastIndexOf(File.separatorChar);
				StringBuilder buf = new StringBuilder();
				if (!dumpDir.equals(".")) {
					buf.append(dumpDir);
					buf.append(File.separatorChar);
				}
				String dir = buf.toString();
				if (index != -1) {
					dir += targetClassName.substring(0, index);
				}
				new File(dir).mkdirs();
				String file;
				if (index != -1) {
					file = targetClassName.substring(index + 1);
				} else {
					file = targetClassName;
				}
				file += ".class";
				new File(dir).mkdirs();
				File out = new File(dir, file);
				FileOutputStream fos = new FileOutputStream(out);
				try {
					fos.write(code);
				} finally {
					fos.close();
				}
			} catch (Exception exp) {
				exp.printStackTrace();
			}
		}
	}

	static boolean isDebug() {
		return debugMode;
	}

	static void debugPrint(String msg) {
		if (isDebug())
			System.out.println("btrace DEBUG: " + msg);
	}

	static void debugPrint(Throwable th) {
		if (!isDebug()) {
			return;
		}

		System.out.println("btrace DEBUG: " + th);
		th.printStackTrace();
	}

	public static String getPassword() {
		return password;
	}

	public static void setPassword(String password) {
		Main.password = password;
	}

	public static boolean isDebugMode() {
		return debugMode;
	}

	public static void setDebugMode(boolean debugMode) {
		Main.debugMode = debugMode;
	}

	public static boolean isDumpClasses() {
		return dumpClasses;
	}

	public static void setDumpClasses(boolean dumpClasses) {
		Main.dumpClasses = dumpClasses;
	}

	public static String getDumpDir() {
		return dumpDir;
	}

	public static void setDumpDir(String dumpDir) {
		Main.dumpDir = dumpDir;
	}
}
